home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Essentials / Developer Essentials Jul 90 / DTS Sample Code / Apple II Sample Code / MPW IIGS SC / SC.004.Custom.Control / BoxCtrl.DefProc.aii < prev    next >
Encoding:
Text File  |  1990-06-25  |  45.4 KB  |  1,862 lines  |  [TEXT/MPS ]

  1. *******************************************************************************
  2. *
  3. * BoxCtl CDEFProc -- Version 3.0
  4. *
  5. * (C)  Copyright Apple Computer, Inc. 1988-1990
  6. * All rights reserved.
  7. *
  8. * Developer Technical Support Apple II Sample Code
  9. *
  10. * by Keith Rollin
  11. *
  12. * This is a Custom Control for the Apple IIGS. It is similar in appearance
  13. * and action to the 'resize' box you get when clicking on an object in GS Draw
  14. * or MacDraw.
  15. *
  16. * The Control is essentially a frame with 4 or 8 grow knobs on the corners
  17. * and/or edges. Dragging on any of these knobs will grow the control. Dragging
  18. * on the frame will move the entire control (like moving a window). A flag can
  19. * be set so that the control will be dragged if the user clicks in the interior
  20. * of the control as well. Part codes for the knobs and frame are listed below.
  21. *
  22. * Snapping the rectangle's coordinates to a grid is also supported. The
  23. * rectangle is snapped to the nearest point on the grid. This effectively means
  24. * that I perform rounding, and not truncating.
  25. *
  26. * The frame of the control is the rectangle passed to NewControl. The size of
  27. * the knobs and the size of the grid are passed in ctlValue. The 'interior drag
  28. * flag' is passed in ctlFlags. See below for details.
  29. *
  30. * Color is supported. Currently, only two colors are used: one for the frame,
  31. * and one for the knobs. The default colors are black.
  32. *
  33. *******************************************************************************
  34. *
  35. *   Part Code returned:
  36. *
  37. *           $A0 for all parts of the control (all parts are 'indicators')
  38. *
  39. *
  40. *   CtlValue:   Bits  8-15: width of grow knobs
  41. *               Bits  0- 7: height of grow knobs
  42. *
  43. *   CtlFlag bits:
  44. *               7 = 1 - Control is invisible
  45. *                 = 0 - Control is visible
  46. *               2 = 1 - Has edge knobs
  47. *                 = 0 - Doesn't
  48. *               1 = 1 - Has corner knobs
  49. *                 = 0 - Doesn't
  50. *               0 = 1 - Clicking in interior will drag control
  51. *                 = 0 - Clicking in interior does nothing
  52. *
  53. *   Data:   Contains a pointer to the following data block. This data
  54. *           block holds the following information, which is copied to
  55. *           the end    of the control record:
  56. *
  57. *               WORD: Min Y when growing
  58. *               WORD: Min X when growing
  59. *               WORD: Max Y when growing
  60. *               WORD: Max X when growing
  61. *               WORD: Spacing for Y segment of grid
  62. *               WORD: Spacing for X segment of grid
  63. *
  64. *   Color Table:
  65. *       WORD: Bits  12-15:  <reserved>
  66. *     8-11:  Knob Color
  67. *     4- 7:  Inactive Frame/Knob Color
  68. *     0- 3:  Frame Color
  69. *
  70. *******************************************************************************
  71. *
  72. *   How to use the Control:
  73. *
  74. *       Initialization and tracking of the control work just like any other;
  75. *       set it up with NewControl. When GetNextEvent returns inContent, call
  76. *       FindControl to see if you hit the control. If you did, call
  77. *       TrackControl.
  78. *
  79. *   Initialization:
  80. *
  81. *               pha        ; space for result
  82. *               pha
  83. *               PushLong theWindow
  84. *               PushLong #theRect    ; pointer to bounding rectangle
  85. *               PushLong #0    ; no title
  86. *               PushWord #%00000111    ; vis, int will drag, corners & edges
  87. *               PushWord #$0503    ; Width = 5/Height = 3
  88. *               PushLong #CtrlData    ; Pointer to additional data
  89. *               PushLong #BoxProc    ; pointer to DefProc
  90. *               PushLong #0    ; refcon
  91. *               PushLong #0    ; use std color table
  92. *               _NewControl
  93. *               PullLong theControl
  94. *
  95. *   theRect     dc.w    50,140,110,300
  96. *   CtrlData    dc.w    10      ; min Y
  97. *               dc.w    10      ; min X
  98. *               dc.w    100     ; max Y
  99. *               dc.w    200     ; max X
  100. *               dc.w    16      ; grid Y
  101. *               dc.w    32      ; grid X
  102. *
  103. *******************************************************************************
  104. **********************************************************************
  105. *                                                                    *
  106. *     This program and its derivatives are licensed only for         *
  107. *     use on Apple computers.                                        *
  108. *                                                                    *
  109. *     Works based on this program must contain and                   *
  110. *     conspicuously display this notice.                             *
  111. *                                                                    *
  112. *     This software is provided for your evaluation and to           *
  113. *     assist you in developing software for the Apple IIGS           *
  114. *     computer.                                                      *
  115. *                                                                    *
  116. *     DISCLAIMER OF WARRANTY                                         *
  117. *                                                                    *
  118. *     THE SOFTWARE IS PROVIDED "AS IS" WITHOUT                       *
  119. *     WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,               *
  120. *     WITH RESPECT TO ITS MERCHANTABILITY OR ITS FITNESS             *
  121. *     FOR ANY PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO             *
  122. *     THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH            *
  123. *     YOU.  SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU (AND            *
  124. *     NOT APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE)               *
  125. *     ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING,             *
  126. *     REPAIR OR CORRECTION.                                          *
  127. *                                                                    *
  128. *     Apple does not warrant that the functions                      *
  129. *     contained in the Software will meet your requirements          *
  130. *     or that the operation of the Software will be                  *
  131. *     uninterrupted or error free or that defects in the             *
  132. *     Software will be corrected.                                    *
  133. *                                                                    *
  134. *     SOME STATES DO NOT ALLOW THE EXCLUSION                         *
  135. *     OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY              *
  136. *     NOT APPLY TO YOU.  THIS WARRANTY GIVES YOU SPECIFIC            *
  137. *     LEGAL RIGHTS AND YOU MAY ALSO HAVE OTHER RIGHTS                *
  138. *     WHICH VARY FROM STATE TO STATE.                                *
  139. *                                                                    *
  140. *                                                                    *
  141. **********************************************************************
  142.     eject
  143. ;-------------------------------------------
  144. ;
  145. ; --- Constants
  146. ;
  147. ; --- Stack Frame/Local Direct Page usage
  148. ;
  149.  
  150. OrigD    equ 1    ; caller's saved direct page register
  151. OrigB    equ OrigD+2    ; caller's saved data bank register
  152. work    equ OrigB+1    ; general use work space
  153. CtlPtr    equ work+4    ; pointer to control record
  154. RtnAddr    equ CtlPtr+4    ; RTL address back to Control Manager
  155. theCtlHandle    equ RtnAddr+3    ; handle to control record
  156. CtlParam    equ theCtlHandle+4    ; add'l parameter passed to DefProc
  157. CtlCode    equ CtlParam+4    ; operation to perform
  158. ReturnValue    equ CtlCode+2    ; space for return value to Ctl Mgr.
  159.  
  160. ;
  161. ; --- Offsets for my control record
  162. ;
  163.  
  164. oCtlMinY    equ octlColor+4
  165. oCtlMinX    equ oCtlMinY+2
  166. oCtlMaxY    equ oCtlMinX+2
  167. oCtlMaxX    equ oCtlMaxY+2
  168. oCtlGridY    equ oCtlMaxX+2
  169. oCtlGridX    equ oCtlGridY+2
  170. oCtlSize    equ oCtlGridX+2
  171.  
  172. ;
  173. ; --- Part codes for my control
  174. ;
  175.  
  176. BoxCtlPart    equ $A0    ; This is returned to the app.
  177.  
  178. FramePart    equ $01    ; These are used internally to tell
  179. InteriorPart    equ $02    ; 'myDragCtl' what part of the
  180. ULPart    equ $03    ; control we are dragging.
  181. TopPart    equ $04
  182. URPart    equ $05
  183. RightPart    equ $06
  184. LLPart    equ $07
  185. BottomPart    equ $08
  186. LRPart    equ $09
  187. LeftPart    equ $0A
  188.  
  189.  
  190. ;
  191. ; --- Masks for ctlFlags
  192. ;
  193.  
  194. dragIntMask    equ %00000001    ; if set, let user drag on interior
  195. knobCornerMask    equ %00000010    ; if set, draw knobs on the corners
  196. knobEdgeMask    equ %00000100    ; if set, draw knobs on the edges
  197. visMask    equ ctlInVis
  198.  
  199.     EJECT
  200. *******************************************************************************
  201. *
  202. CtlData    RECORD
  203. *
  204. * Description:    Storage for CDEF
  205. *
  206. *
  207. * External Refs:    NONE
  208. *
  209. * Entry Points:    NONE
  210. *
  211. *******************************************************************************
  212.  
  213. deltaX    ds.b 2    ; used when growing the control
  214. deltaY    ds.b 2
  215. ColorPtr    ds.b 4    ; pointer to actual color table to use
  216. theParam    ds.b 4    ; copy of CtlParam passed on stack
  217. dragPart    ds.b 2    ; internal part of control hit
  218.  
  219. myCtlRect    ds.b 8    ; copy of CtlRect in control record
  220. myCtlGridY    ds.b 2    ; copy of CtlGridY
  221. myCtlGridX    ds.b 2    ; copy of CtlGridX
  222.  
  223. FrameHeight    ds.b 2
  224. FrameWidth    ds.b 2
  225.  
  226. knobUL    ds.b 8    ; This space is used to store the
  227. knobTop    ds.b 8    ; rectangles that define the grow
  228. knobUR    ds.b 8    ; knobs on the control. They are
  229. knobRight    ds.b 8    ; created as needed, and are not
  230. knobLR    ds.b 8    ; stored in the control record.
  231. knobBottom    ds.b 8
  232. knobLL    ds.b 8
  233. knobLeft    ds.b 8
  234.  
  235. StdColorTable    dc.b $C0    ; grey inact frame, blk normal frame
  236.     dc.b $00    ; black knobs
  237. StdCtlData    dc.w 10,10,200,640,0,0 ; min 10,10; max 200,640; no grid
  238. oldPenState    ds.b $32    ; storage for old pen state.
  239.  
  240. GreyPattern    dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
  241.     dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
  242.     dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
  243.     dc.b $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC
  244.  
  245.     ENDR
  246.  
  247.     EJECT
  248. *******************************************************************************
  249. *
  250. BoxProc    PROC
  251. *
  252. * Description:    Main Routine for Custom Control.
  253. *
  254. *
  255. * Inputs:    On entry, the parameters are passed to us on the stack.
  256. *
  257. *    |    | Previous Contents
  258. *    |-------------------|
  259. *    |    ReturnValue    | LONG - Space for return value
  260. *    |-------------------|
  261. *    |    CtlCode    | WORD - operation to perform
  262. *    |-------------------|
  263. *    |    CtlParam    | LONG - add'l parameter
  264. *    |-------------------|
  265. *    |    theCtlHandle    | LONG - Handle to control record
  266. *    |-------------------|
  267. *    |    RtnAddr    | 3 BYTES - RTL address
  268. *    |-------------------|
  269. *    |    | <-- Stack pointer
  270. *
  271. * Outputs:    Put something into ReturnValue, pull off the parameters,
  272. *    and return to the Control Manager.
  273. *
  274. *    |    | Previous Contents
  275. *    |-------------------|
  276. *    |    ReturnValue    | LONG - Space for return value
  277. *    |-------------------|
  278. *    |    RtnAddr    | 3 BYTES - RTL address
  279. *    |-------------------|
  280. *    |    | <-- Stack pointer
  281. *
  282. * External Refs:
  283.     import InitCtlData
  284.     import myDrawCtl
  285.     import myTestCtl
  286.     import myInitCtl
  287.     import myDragCtl
  288.     import myNewValue
  289.     import mySetParams
  290.     import myRecSize
  291.     import Null
  292. *
  293. * Entry Points:    NONE
  294. *
  295. *******************************************************************************
  296.     with CtlData
  297.  
  298. ;
  299. ; We are going to be using the stack as our direct page for the
  300. ; custom control. The equates listed at the beginning of the custom
  301. ; control (right after the pageful of comments that explain it)
  302. ; describe the layout of the stack frame.
  303. ;
  304. ; The way we set up the stack frame as our direct page is by first
  305. ; saving the old contents of the DP register, transfering the stack
  306. ; pointer to the accumulator, and then transferring that to the DP
  307. ; register. We also save the contents of the Data Bank Register and
  308. ; reset it to the Program Bank Register so that we don't have to
  309. ; use 3-byte long addressing anywhere. We will restore both of these
  310. ; registers when we leave the custom control.
  311. ;
  312.  
  313.     pha    ; push on some room    for 'CtlPtr'
  314.     pha
  315.     pha    ; push on some room    for 'work'
  316.     pha
  317.  
  318.     phb    ; save the Data Bank register
  319.     phd    ; save the Direct Page register
  320.  
  321.     phk    ; switch data bank to program bank
  322.     plb
  323.     tsc    ; switch Direct Page into stack
  324.     tcd
  325.  
  326.     jsr InitCtlData    ; init some handy data
  327.  
  328.     lda <CtlCode    ; get routine # to call
  329.     cmp #recSize+1    ; we only know of 12 CtlCodes
  330.     blt ShiftIt
  331.     lda #6    ; force unknown codes to null events
  332. ShiftIt
  333.     asl a
  334.     tax
  335.     jsr (CtlTable,x)
  336.  
  337.     sta <ReturnValue    ; save the return value
  338.     stx <ReturnValue+2
  339.  
  340. ;
  341. ; The Return Value has been stored on the stack, and it is time for us to
  342. ; return back to the Control Manager. Before we do so, however, we must
  343. ; remove the parameters that were passed to us on the stack. We do this
  344. ; by moving the RTL address up just below the Return Value, getting the
  345. ; stack pointer, and adding an amount to it so that we point to where
  346. ; the RTL address has been moved to. We can then simply RTL back to the
  347. ; Control Manager, and it will pick up the Return Value right off of the
  348. ; stack!
  349. ;
  350.     lda <RtnAddr    ; move the return address up
  351.     sta <ReturnValue-3
  352.     lda <RtnAddr+1
  353.     sta <ReturnValue-2
  354.  
  355.     tsc    ; Get the stack pointer
  356.  
  357.     pld    ; restore caller's Data Bank and
  358.     plb    ; Direct Page registers
  359.  
  360.     clc    ; Adjust the stack pointer to point to
  361.     adc #ReturnValue-4    ; the new location of the RTL address.
  362.     tcs    ; Put the stack pointer back.
  363.  
  364.     rtl    ; back to the caller
  365.  
  366.  
  367. ; This is a table of pointers to routines to be called for specific
  368. ; CtlCodes passed to us. The 'null' entries are pointers to a
  369. ; routine that does nothing except return 'no error'.
  370.  
  371. CtlTable
  372.     dc.w myDrawCtl    ; 0 drawCtl
  373.     dc.w Null    ; 1 calcCRect
  374.     dc.w myTestCtl    ; 2 testCtl
  375.     dc.w myInitCtl    ; 3 initCtl
  376.     dc.w Null    ; 4 dispCtl
  377.     dc.w Null    ; 5 posCtl
  378.     dc.w Null    ; 6 thumbCtl
  379.     dc.w myDragCtl    ; 7 dragCtl
  380.     dc.w Null    ; 8 autoTrack
  381.     dc.w myNewValue    ; 9 newValue
  382.     dc.w mySetParams    ;10 setParams
  383.     dc.w myInitCtl    ;11 moveCtl
  384.     dc.w myRecSize    ;12 recSize
  385.  
  386.     ENDP
  387.  
  388.     EJECT
  389. *******************************************************************************
  390. *
  391. myDrawCtl    PROC
  392. *
  393. * Description:    Draw control command.
  394. *
  395. *
  396. * Inputs:    NONE
  397. *
  398. * Outputs:    NONE
  399. *
  400. * External Refs:
  401.     import CalcCorners
  402.     import SetMyPen
  403. *
  404. * Entry Points:    NONE
  405. *
  406. *******************************************************************************
  407.     with CtlData
  408.  
  409. ; figure out the coordinates of the grow knobs.
  410.  
  411.     jsr CalcCorners
  412.  
  413. ; Save the pen state, as we'll be changing it a lot.
  414.  
  415.     PushLong #oldPenState
  416.     _GetPenState
  417.  
  418. ; Set the correct width of the pen for the mode we are in.
  419.  
  420.     jsr SetMyPen
  421.  
  422. ;
  423. ; Set the local variable 'ActivFlag' to indicate how to draw the
  424. ; control. We draw it inactive if a) the Hilite value is 255, or b)
  425. ; the owner window is inactive and fCtrlTie is set.
  426. ;
  427.     ldy #octlHilite    ; are we inactive?
  428.     lda [<CtlPtr],y
  429.     and #$00FF
  430.     cmp #$00FF
  431.     beq DrawInactive    ; yes, so clear ActivFlag
  432.  
  433. ; At this point, the control can potentially be drawn as an active
  434. ; control. But this can only happen if the window is active, or is
  435. ; inactive but with the fCtlTie flag clear.
  436.  
  437.     ldy #octlOwner    ; get the pointer to the control's
  438.     lda [<CtlPtr],y    ; owning window into 'work'
  439.     sta <work
  440.     iny
  441.     iny
  442.     lda [<CtlPtr],y
  443.     sta <work+2
  444.  
  445.     ldy #owFrame-4    ; get the frame bits
  446.     lda [<work],y
  447.     and #fHilited    ; check the Hilite bit
  448.     bne DrawActive    ; window is active -> control active
  449.     lda [<work],y    ; window is inactive
  450.     and #fCtlTie    ; does CtlTie bit affect control?
  451.     bne DrawInactive    ; yes, so draw it inactive too
  452.  
  453. DrawActive    lda #1
  454.     sta ActivFlag
  455.     bra SetColorTable
  456.  
  457. DrawInactive    stz ActivFlag
  458.  
  459. ; Put the color table pointer into a  Direct page location.
  460.  
  461. SetColorTable
  462.     lda ColorPtr
  463.     sta <work
  464.     lda ColorPtr+2
  465.     sta <work+2
  466.  
  467. ;
  468. ; See if the control is active or not, and set the color
  469. ; accordingly.
  470. ;
  471.  
  472.     lda ActivFlag
  473.     bne SetFrameColor
  474.  
  475.     lda [<work]    ; YES - so we are. Use the inactive
  476.     lsr a    ; pattern for this.
  477.     lsr a
  478.     lsr a
  479.     lsr a
  480.     and #$000f
  481.     pha
  482.     _SetSolidPenPat
  483.  
  484.     bra DrawFrame
  485.  
  486. SetFrameColor
  487.     lda [<work]    ; get the color for the frame from
  488.     and #$000f    ; the color table and use it.
  489.     pha
  490.     _SetSolidPenPat
  491.  
  492. DrawFrame
  493.     PushLong #myCtlRect    ; draw the frame of the control
  494.     _FrameRect
  495.  
  496. ;
  497. ; See if the control is active or not, and set the color accordingly. If
  498. ; we so chose we could even decide not to draw knobs on an inactive sizer.
  499. ; This is similar to the scrollbar defproc no drawing the thumb on an
  500. ; inactive scrollbar.
  501. ;
  502.  
  503.     lda ActivFlag
  504.     beq DrawKnobs    ; it's inactive; keep that pattern
  505.  
  506.     lda [<work]    ; get the color of the knobs from
  507.     xba    ; the color table and set it.
  508.     and #$000f
  509.     pha
  510.     _SetSolidPenPat
  511.  
  512. DrawKnobs
  513.     ldy #octlFlag    ; do we need corner knobs?
  514.     lda [<CtlPtr],y
  515.     and #knobCornerMask
  516.     beq ckEdges    ; no, so check for edge knobs.
  517.  
  518.     PushLong #knobUL    ; yes, so draw them
  519.     _PaintRect
  520.  
  521.     PushLong #knobUR
  522.     _PaintRect
  523.  
  524.     PushLong #knobLL
  525.     _PaintRect
  526.  
  527.     PushLong #knobLR
  528.     _PaintRect
  529.  
  530. ckEdges
  531.     ldy #octlFlag    ; do we need edge knobs?
  532.     lda [<CtlPtr],y
  533.     and #knobEdgeMask
  534.     beq done    ; no, so leave
  535.  
  536.     PushLong #knobTop    ; yes, so draw them.
  537.     _PaintRect
  538.  
  539.     PushLong #knobRight
  540.     _PaintRect
  541.  
  542.     PushLong #knobBottom
  543.     _PaintRect
  544.  
  545.     PushLong #knobLeft
  546.     _PaintRect
  547.  
  548. done
  549.     PushLong #oldPenState ; clean up after ourselves.
  550.     _SetPenState
  551.  
  552.     lda #0
  553.     tax
  554.  
  555.     rts
  556.  
  557. ActivFlag    ds.b 2
  558.  
  559.     ENDP
  560.  
  561.     EJECT
  562. *******************************************************************************
  563. *
  564. myTestCtl    PROC
  565. *
  566. * Description:    Hit test command. This routine checks to see if we have
  567. *    chosen the ability to drag the control when we click in
  568. *    the interior or not. If so, then we will always return
  569. *    our partcode (BoxCtlPart = $A0). If not, then we only
  570. *    return our partcode if we click on the frame or a knob.
  571. *
  572. *
  573. * Inputs:    NONE
  574. *
  575. * Outputs:    A = Part code hit
  576. *
  577. * External Refs:
  578.     import TestFrame
  579. *
  580. * Entry Points:    NONE
  581. *
  582. *******************************************************************************
  583.     with CtlData
  584.  
  585.     jsr TestFrame
  586.     cmp #noPart    ; hit anything?
  587.     beq exit    ; no - return nothing
  588.     lda #BoxCtlPart    ; yes - return our partcode
  589. exit
  590.     ldx #0    ; high byte of return value always $00
  591.     rts
  592.  
  593.     ENDP
  594.  
  595.     EJECT
  596. *******************************************************************************
  597. *
  598. myInitCtl    PROC
  599. *
  600. * Description:    Called to initialize my custom fields of the control
  601. *    record. It calles mySetParams to handle the data that
  602. *    is pointed to by the Data/Params field, and makes sure
  603. *    that the corners of the rectangle lie on the grid.
  604. *
  605. *
  606. * Inputs:    NONE
  607. *
  608. * Outputs:    NONE
  609. *
  610. * External Refs:
  611.     import mySetParams
  612.     import Snap2Grid
  613.     import SetCtlRect
  614.     import InitCtlData
  615. *
  616. * Entry Points:    NONE
  617. *
  618. *******************************************************************************
  619.     with CtlData
  620.  
  621. ; First, set up some fields based on the value of Param. This is
  622. ; a pointer to some additional data, so what SetParams does is
  623. ; copy that into our control record.
  624.  
  625.     jsr mySetParams
  626.     jsr InitCtlData
  627.  
  628. ; Next, make sure the edges of the control are aligned to a grid.
  629.  
  630.     ldx #^myCtlRect
  631.     lda #myCtlRect
  632.     jsr Snap2Grid
  633.  
  634.     jsr SetCtlRect
  635.  
  636.     lda #0    ; return nothing
  637.     txa
  638.  
  639.     rts
  640.     ENDP
  641.  
  642.     EJECT
  643. *******************************************************************************
  644. *
  645. myDragCtl    PROC
  646. *
  647. * Description:    Drag command. We hit something. Determine what it was and
  648. *    drag it around the screen. If we hit the frame, then drag
  649. *    the whole control. If we hit a knob, then we grow the
  650. *    control (just like growing a window).
  651. *
  652. *
  653. * Inputs:    NONE
  654. *
  655. * Outputs:    NONE
  656. *
  657. * External Refs:
  658.     import SetMyPen
  659.     import Snap2Grid
  660.     import myDrawCtl
  661.     import FindPart
  662.     import SetCtlRect
  663. *
  664. * Entry Points:    NONE
  665. *
  666. *******************************************************************************
  667.     with CtlData
  668.  
  669.     PushLong #oldPenState ; save drawing state, as we change it
  670.     _GetPenState
  671.  
  672.     ldy #6    ; make a working copy of CtlRect.
  673. loop100    lda myCtlRect,y
  674.     sta drag_rect,y
  675.     dey
  676.     dey
  677.     bpl loop100
  678.  
  679.     pha    ; has the mouse been lifted up?
  680.     PushWord #mUpMask    ; ask for any such events
  681.     PushLong #TrackEvent
  682.     _GetNextEvent
  683.     pla
  684.  
  685.     lda TrackEvent+owhat
  686.     cmp #mouseUpEvt    ; was there a mouse up event?
  687.     bne mDown    ; no - so start tracking
  688.     brl done    ; yes - so leave
  689.  
  690. mDown
  691.     PushLong #TrackEvent+owhere
  692.     _GlobalToLocal
  693.  
  694.     lda TrackEvent+owhere
  695.     sta OldMouse    ; FindPart likes the mouse Position
  696.     sta theParam    ; in 'theParam'
  697.     lda TrackEvent+owhere+2
  698.     sta OldMouse+2
  699.     sta theParam+2
  700.  
  701.     jsr FindPart    ; what did we hit? (returns internal
  702.     sta dragPart    ;   partcode number).
  703.  
  704.     cmp #InteriorPart+1    ; what part did we hit?
  705.     bge GrowFrame    ; some knob - grow the control
  706.     brl DragFrame    ; interior or frame - move the control
  707.  
  708. ;
  709. ; This part of the program is called when we detect that we have clicked on
  710. ; a grow knob. It tracks the motions of the mouse, and grows/draws the
  711. ; control appropriately.
  712. ;
  713. GrowFrame
  714.     PushWord #2    ; set us to XOR mode. This is for the
  715.     _SetPenMode    ; rubber-banding effect of growing.
  716.  
  717.     jsr SetMyPen    ; set my pen's width
  718.  
  719.     PushLong #GreyPattern ; grow with a grey pattern
  720.     _SetPenPat
  721.  
  722.     ldy #oCtlMaxX    ; get local copies of the maximum
  723.     ldx #6    ; and minimum dimensions.
  724. loop0010    lda [<CtlPtr],y
  725.     sta MinY,x
  726.     dey
  727.     dey
  728.     dex
  729.     dex
  730.     bpl loop0010
  731.  
  732. ; validate the min and max values. the min values must be at least $10.
  733. ; if not, they are set there. the max values must be at least as large
  734. ; as the min values. if not, they are set to $FFFF
  735.  
  736.     lda #$10
  737.     cmp MinX
  738.     blt ckMinY
  739.     sta MinX
  740.  
  741. ckMinY    cmp MinY
  742.     blt ckMaxX
  743.     sta MinY
  744.  
  745. ckMaxX    lda MaxX
  746.     cmp MinX
  747.     bge ckMaxY
  748. ChgX    lda #$FFFF
  749.     sta MaxX
  750.  
  751. ckMaxY    lda MaxY
  752.     cmp MinY
  753.     bge doneCk
  754. ChgY    lda #$FFFF
  755.     sta MaxY
  756. doneCk
  757.  
  758. ;
  759. ; We grow the control with the following algorithm. First, setup by:
  760. ;
  761. ;  1. saving the current mouse postion in 'NewMouse',
  762. ;  2. aligning 'NewMouse' to the grid, and
  763. ;  3. making a working copy of the control's rectangle in 'drag_rect'.
  764. ;
  765. ; We then enter the main loop:
  766. ;
  767. ;  4. Draw a copy of the rubber-banding rectangle.
  768. ;  5. Check the mouse button. If it is up, goto #12
  769. ;  6. Put the position of the mouse into 'NewerMouse'. Align it to grid.
  770. ;  7. Check it against 'NewMouse'. If it hasn't changed, goto #5.
  771. ;  8. Erase the old rubber-Band.
  772. ;  9. Calculate the new rectangle and put it into 'drag_rect'
  773. ; 10. Move 'NewerMouse' into 'NewMouse'
  774. ; 11. Goto #4
  775. ;
  776. ; The growing is all done. Clean up and leave.
  777. ;
  778. ; 12. Erase the rubber band.
  779. ; 13. Erase the control.
  780. ; 14. Invalidate the area under the control
  781. ; 15. Make drag_rect the new boundary of the control
  782. ; 16. Say goodnite, Gracie.
  783. ;
  784.  
  785.     lda OldMouse
  786.     sta NewMouse
  787.     lda OldMouse+2
  788.     sta NewMouse+2
  789.  
  790.     ldx #^NewMouse    ; make sure we are on the grid!!!
  791.     lda #NewMouse
  792.     jsr Snap2Grid
  793.  
  794. DrawLoop
  795.     PushLong #drag_rect    ; draw the rubber-band
  796.     _FrameRect
  797.  
  798. WaitLoop
  799.     pha    ; has the mouse been lifted up?
  800.     PushWord #mUpMask    ; ask for any such events
  801.     PushLong #TrackEvent
  802.     _GetNextEvent
  803.     pla
  804.  
  805.     lda TrackEvent+owhat
  806.     cmp #mouseUpEvt
  807.     beq done    ; mouse up occured - so leave.
  808.     
  809.     lda TrackEvent+owhere ; mouse still down. copy it to
  810.     sta NewerMouse    ; NewerMouse
  811.     lda TrackEvent+owhere+2
  812.     sta NewerMouse+2
  813.     
  814.     PushLong #NewerMouse ; convert it to window coordinates.
  815.     _GlobalToLocal
  816.  
  817.     ldx #^NewerMouse    ; make sure the new position is on
  818.     lda #NewerMouse    ; the grid.
  819.     jsr Snap2Grid
  820.  
  821.     lda NewerMouse    ; See if the mouse position moved. We
  822.     cmp NewMouse    ; only redraw the rubber-band if it did.
  823.     bne ReDraw    ; It did - redraw the rubber-band
  824.     lda NewerMouse+2
  825.     cmp NewMouse+2
  826.     beq WaitLoop    ; No movement - check button state
  827.  
  828. ReDraw
  829.     PushLong #drag_rect    ; erase the old rubber band
  830.     _FrameRect
  831.  
  832.     lda NewerMouse    ; copy NewerMouse for next loop
  833.     sta NewMouse
  834.     sec    ; and update deltaXY for AdjFrameRect
  835.     sbc OldMouse
  836.     sta deltaY
  837.  
  838.     lda NewerMouse+2
  839.     sta NewMouse+2
  840.     sec
  841.     sbc OldMouse+2
  842.     sta deltaX
  843.  
  844.     jsr AdjFrameRect    ; adjust for drawing the new rect
  845.  
  846.     brl DrawLoop    ; redraw the rubber-band
  847.  
  848. done
  849.     PushLong #drag_rect    ; erase the rubber band
  850.     _FrameRect
  851.  
  852.     PushLong #myCtlRect    ; erase the control
  853.     _EraseRect
  854.  
  855.     PushLong #myCtlRect    ; invalidate the stuff under it
  856.     _InvalRect
  857.  
  858.     brl Exit
  859. ;
  860. ; This part of the program is called when we have clicked on either the
  861. ; frame or the interior. It calls dragrect to move an outline of the
  862. ; control around.
  863. ;
  864.  
  865. DragFrame
  866.  
  867. ; Get the portRect of the window that owns this control. This rectangle
  868. ; will be used to control the limits of dragging.
  869.  
  870.     ldy #octlOwner    ; get the pointer to the control's
  871.     lda [<CtlPtr],y    ; owning window into 'work'
  872.     sta <work
  873.     iny
  874.     iny
  875.     lda [<CtlPtr],y
  876.     sta <work+2
  877.  
  878.     ldy #oportRect+6    ; copy the PortRect of the window
  879.     ldx #6    ; out of its GrafPort into limitRect.
  880. loop400    lda [<work],y
  881.     sta limitRect,x
  882.     dey
  883.     dey
  884.     dex
  885.     dex
  886.     bpl loop400
  887.  
  888. ;
  889. ; Use dragRect to drag an outline of the control around the
  890. ; screen for me.
  891. ;
  892.     pha    ; space for result
  893.     pha
  894.     PushLong #DragDraw    ; no action proc
  895.     PushLong #GreyPattern ; drag pattern
  896.     PushLong OldMouse    ; starting location of mouse
  897.     PushLong #drag_rect    ; outline to drag
  898.     PushLong #limitRect    ; limitRect
  899.     PushLong #limitRect    ; slopRect
  900.     PushWord #%00101000    ; custom drag flag, ReturnRect flag
  901.     _DragRect
  902.  
  903.     pla    ; pull off and discard the deltas
  904.     pla
  905.  
  906.     PushLong #myCtlRect    ; erase the control in its old pos'n
  907.     _EraseRect
  908.  
  909.     PushLong #myCtlRect    ; invalidate the stuff under it
  910.     _InvalRect
  911.  
  912.     ldx #^drag_rect    ; align the new rectangle to the grid.
  913.     lda #drag_rect
  914.     jsr Snap2Grid
  915.  
  916. Exit
  917.     ldy #6    ; copy over the new    control frame
  918. loop200    lda drag_rect,y
  919.     sta myCtlRect,y
  920.     dey
  921.     dey
  922.     bpl loop200
  923.  
  924.     PushLong #myCtlRect    ; Invalidate the new area under the
  925.     _InvalRect    ; control so it will be redrawn.
  926.  
  927.     PushLong #oldPenState
  928.     _SetPenState    ; clean up QuickDraw after ourselves.
  929.  
  930.     jsr SetCtlRect    ; copy new rect into control record
  931.  
  932.     lda #-1    ; Say that we handled it
  933.     tax
  934.  
  935.     rts
  936.  
  937. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  938. ;
  939. ; Called by DragRect to draw the rectangle of the control. I draw the outline
  940. ; myself so that I can align it to my own grid. The normal aligning of the
  941. ; rectangle to the grid only aligns to powers of 2. I align to anything.
  942. ;
  943. ; When this routine is called, the stack looks like this:
  944. ;
  945. ;    PUSH:WORD - delta X
  946. ;    PUSH:WORD - delta Y
  947. ;    PUSH:BYTE[3] - return address
  948. ;
  949. ; However, since I set the ReturnRect flag, I can ignore the deltas passed to
  950. ; me, and use drag_rect directly. I make a copy of it, make sure that it is
  951. ; aligned to the grid, draw it, remove the parameters, and return to the
  952. ; control manager.
  953. ;
  954.  
  955. DragDraw
  956.  
  957. RTLAddr    equ 1
  958. DY    equ RTLAddr+3
  959. DX    equ DY+2
  960.  
  961.     ldx #6    ; make a copy of the rectangle that
  962. loop500    lda drag_rect,x    ; _DragRect is passing to us. We are
  963.     sta draw_rect,x    ; going to modify it so that it is
  964.     dex    ; aligned to the grid.
  965.     dex
  966.     bpl loop500
  967.  
  968.     ldx #^draw_rect    ; align the new rectangle to the grid.
  969.     lda #draw_rect
  970.     jsr Snap2Grid
  971.  
  972. DrawOutline
  973.     PushLong #draw_rect
  974.     _FrameRect
  975.  
  976. ExitDragDraw
  977.     lda 1,s    ; move the RTL address back up
  978.     sta 5,s
  979.     lda 2,s
  980.     sta 6,s
  981.  
  982.     pla    ; remove the parameters (DX, DY)
  983.     pla
  984.  
  985.     rtl    ; back to dragRect!!
  986.  
  987. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  988. ;
  989. ; Called by the routine that grows the control with a rubber-band
  990. ; sort of thing. It uses the part code we hit as an index into
  991. ; a table. This table contains a list of offsets into the
  992. ; 4 words of drag_rect. It reads in a pair of these offsets and
  993. ; uses them to modify the appropriate X/Y coordinates with
  994. ; deltaY and deltaX. DeltaY and deltaX are set up by the
  995. ; rubber-banding routine.
  996.  
  997. AdjFrameRect
  998.     ldx #6
  999. loop380    lda drag_rect,x
  1000.     sta new_rect,x
  1001.     dex
  1002.     dex
  1003.     bpl loop380
  1004.  
  1005.     lda dragPart    ; get the part code we are dragging
  1006.     sec
  1007.     sbc #ULPart
  1008.     asl a    ; turn it into an index.
  1009.     asl a
  1010.     tax
  1011.     lda PtToAdj,x    ; get Y coordinate to adjust
  1012.     tay
  1013.     lda PtToAdj+2,x    ; get X coordinate to adjust
  1014.     tax
  1015.  
  1016. ; if the entry that was fetched from the table was -1, then that means
  1017. ; that we are making no adjustments in that direction. For instance, it
  1018. ; we are dragging on the top edge knob, we want to adjust just the top
  1019. ; side of the rectangle, but want to leave either of the sides alone.
  1020.  
  1021.     cpy #-1     ; modify the vertical coordinate
  1022.     beq NoChg1    ; of choice if necessary.
  1023.     clc
  1024.     lda deltaY
  1025.     adc myCtlRect,y
  1026.     sta new_rect,y
  1027.  
  1028. NoChg1    cpx #-1    ; modify the horizontal coordinate
  1029.     beq NoChg2    ; of choice if necessary.
  1030.     clc
  1031.     lda deltaX
  1032.     adc myCtlRect,x
  1033.     sta new_rect,x
  1034.  
  1035. NoChg2
  1036.     ldx #^new_rect    ; align the rectangle to the grid.
  1037.     lda #new_rect
  1038.     jsr Snap2Grid
  1039.  
  1040. ; now that we have a new rectangle, we have to make sure that it is within
  1041. ; the maximum and minimum sizes allowed.
  1042.  
  1043.     sec    ; make sure the new Y size is OK
  1044.     lda new_rect+4
  1045.     sbc new_rect
  1046.     bcc NoCopy1
  1047.     cmp MinY
  1048.     blt NoCopy1    ; it's not, so leave old coords there
  1049.     inc a
  1050.     cmp MaxY
  1051.     bge NoCopy1    ; it's not, so leave old coords there
  1052.  
  1053.     lda new_rect    ; it is, so update it.
  1054.     sta drag_rect
  1055.     lda new_rect+4
  1056.     sta drag_rect+4
  1057.  
  1058. NoCopy1
  1059.     sec    ; make sure the new X size is OK
  1060.     lda new_rect+6
  1061.     sbc new_rect+2
  1062.     bcc NoCopy2
  1063.     cmp MinX
  1064.     blt NoCopy2    ; it's not, so leave old coords there
  1065.     inc a
  1066.     cmp MaxX
  1067.     bge NoCopy2    ; it's not, so leave old coords there
  1068.  
  1069.     lda new_rect+2    ; it is, so update it
  1070.     sta drag_rect+2
  1071.     lda new_rect+6
  1072.     sta drag_rect+6
  1073. NoCopy2
  1074.     rts
  1075.  
  1076.  
  1077. PtToAdj    dc.w 0,2,0,-1,0,6,-1,6,4,6,4,-1,4,2,-1,2
  1078.  
  1079. TrackEvent    ds.b $10
  1080.  
  1081. OldMouse    ds.b 4
  1082. NewMouse    ds.b 8
  1083. NewerMouse    ds.b 8
  1084. drag_rect    ds.b 8
  1085. draw_rect    ds.b 8
  1086. new_rect    ds.b 8
  1087. limitRect    ds.b 8
  1088. MinY    ds.b 2
  1089. MinX    ds.b 2
  1090. MaxY    ds.b 2
  1091. MaxX    ds.b 2
  1092.  
  1093.     ENDP
  1094.  
  1095.     EJECT
  1096. *******************************************************************************
  1097. *
  1098. myNewValue    PROC
  1099. *
  1100. * Description:    Called when the value of the control is to be changed.
  1101. *    The area under the control is invalidated so that it will
  1102. *    be redrawn.
  1103. *
  1104. *
  1105. * Inputs:    NONE
  1106. *
  1107. * Outputs:    NONE
  1108. *
  1109. * External Refs:
  1110.     import Snap2Grid
  1111.     import myDrawCtl
  1112.     import SetCtlRect
  1113. *
  1114. * Entry Points:    NONE
  1115. *
  1116. *******************************************************************************
  1117.     with CtlData
  1118.  
  1119.     PushLong #myCtlRect    ; erase old control
  1120.     _EraseRect
  1121.  
  1122.     PushLong #myCtlRect    ; invalidate area erased
  1123.     _InvalRect
  1124.  
  1125.     ldx #^myCtlRect    ; make sure control is on the grid
  1126.     lda #myCtlRect
  1127.     jsr Snap2Grid
  1128.  
  1129.     jsr SetCtlRect
  1130.  
  1131.     lda #0    ; return no value
  1132.     tax
  1133.  
  1134.     rts
  1135.     ENDP
  1136.  
  1137.     EJECT
  1138. *******************************************************************************
  1139. *
  1140. mySetParams    PROC
  1141. *
  1142. * Description:    Set new parameters command. Don't set if either of them
  1143. *    are negative.
  1144. *
  1145. *
  1146. * Inputs:    NONE
  1147. *
  1148. * Outputs:    NONE
  1149. *
  1150. * External Refs:    NONE
  1151. *
  1152. * Entry Points:    NONE
  1153. *
  1154. *******************************************************************************
  1155.     with CtlData
  1156.  
  1157.     lda theParam    ; these have gotta go on in reverse
  1158.     sta <work+2
  1159.     lda theParam+2
  1160.     sta <work
  1161.  
  1162.     ora <work+2    ; is this a NULL pointer?
  1163.     bne CopyIt    ; no - so use it
  1164.  
  1165.     lda #^StdCtlData    ; yes - so set pointer to default data
  1166.     sta <work+2
  1167.     lda #StdCtlData
  1168.     sta <work
  1169.  
  1170. CopyIt
  1171.     ldy #oCtlSize-2    ; copy the data into control record.
  1172.     ldx #oCtlSize-oCtlMinY-2
  1173. loop    phy
  1174.     txy
  1175.     lda [<work],y
  1176.     ply
  1177.     sta [<CtlPtr],y
  1178.     dey
  1179.     dey
  1180.     dex
  1181.     dex
  1182.     bpl loop
  1183.  
  1184.     lda #0    ; return no value
  1185.     tax
  1186.  
  1187.     rts
  1188.     ENDP
  1189.  
  1190.     EJECT
  1191. *******************************************************************************
  1192. *
  1193. myRecSize    PROC
  1194. *
  1195. * Description:    Return record size command.
  1196. *
  1197. *
  1198. * Inputs:    NONE
  1199. *
  1200. * Outputs:    A = Size of the control record to allocate.
  1201. *
  1202. * External Refs:    NONE
  1203. *
  1204. * Entry Points:    NONE
  1205. *
  1206. *******************************************************************************
  1207.     with CtlData
  1208.  
  1209.     lda #oCtlSize    ; low word of record size
  1210.     ldx #0    ; high word
  1211.  
  1212.     rts
  1213.     ENDP
  1214.  
  1215.     EJECT
  1216. *******************************************************************************
  1217. *
  1218. Null    PROC
  1219. *
  1220. * Description:    Null routine. Does nothing. Returns no error.
  1221. *
  1222. *
  1223. * Inputs:    NONE
  1224. *
  1225. * Outputs:    NONE
  1226. *
  1227. * External Refs:    NONE
  1228. *
  1229. * Entry Points:    NONE
  1230. *
  1231. *******************************************************************************
  1232.  
  1233.     lda #0
  1234.     tax
  1235.  
  1236.     rts
  1237.     ENDP
  1238.  
  1239.     EJECT
  1240. *******************************************************************************
  1241. *
  1242. InitCtlData    PROC
  1243. *
  1244. * Description:    Initialize some data that will needs to be handy: Get a
  1245. *    pointer to the control record (we are supplied only with
  1246. *    a handle), make a local copy of the control rectangle, set
  1247. *    up a pointer to a color table, and move CtlParam from a
  1248. *    Direct Page location into an absolute location.
  1249. *
  1250. *
  1251. * Inputs:    NONE
  1252. *
  1253. * Outputs:    NONE
  1254. *
  1255. * External Refs:    NONE
  1256. *
  1257. * Entry Points:    NONE
  1258. *
  1259. *******************************************************************************
  1260.     with CtlData
  1261.  
  1262. ;
  1263. ; Check to see if the Control Manager is sending us a 'RecSize' call. If it
  1264. ; is, then we cannot rely on the validity of the Control handle, as it has not
  1265. ; yet been allocated and is essentially garbage. Since we do not know what it
  1266. ; points to, we don't want to read from it, as it may actually accidentally
  1267. ; point to something dangerous, like the IWM I/O locations!!! So if we detect
  1268. ; a RecSize code, skip over this routine.
  1269. ;
  1270.     lda <CtlCode    ; is this a RecSize call?
  1271.     cmp #recSize
  1272.     beq done    ; yes - skip this whole routine
  1273.  
  1274. ;
  1275. ; Copy the value in CtlParam into a local location -- off of the direct page.
  1276. ; We do this for convenience. We may need to pass a pointer to CtlParam to
  1277. ; a toolbox routine, but you can't do that for a direct page location. The
  1278. ; number assiciated with a direct page location is really an offset off of the
  1279. ; base specified by the Direct Page register; it is NOT an absolute memory
  1280. ; location. So that we can conveniently use 'PushLong #location' if we need
  1281. ; to, I copy CtlParam to a non-direct page location.
  1282. ;
  1283.     lda <CtlParam
  1284.     sta theParam
  1285.     lda <CtlParam+2
  1286.     sta theParam+2
  1287.  
  1288.     ldy #2    ; get a handy pointer to the CtlRec
  1289.     lda [<theCtlHandle],y
  1290.     sta <CtlPtr+2
  1291.     lda [<theCtlHandle]
  1292.     sta <CtlPtr
  1293.  
  1294.     ldy #octlRect+6    ; copy the control rect over to the
  1295.     ldx #6    ; local variable.
  1296. loop    lda [<CtlPtr],y
  1297.     sta myCtlRect,x
  1298.     dey
  1299.     dey
  1300.     dex
  1301.     dex
  1302.     bpl loop
  1303.  
  1304. ;
  1305. ; The following values are used by Snap2Grid. However, Snap2Grid can be
  1306. ; called with an invalid Direct Page, so we copy them out the control
  1307. ; record (which is pointed to by a pointer in our Direct Page) and into
  1308. ; some absolute locations that we can get to at all times.
  1309. ;
  1310.     ldy #oCtlGridX    ; get the X and Y spacings out of the
  1311.     lda [<CtlPtr],y    ; control record and into some local
  1312.     sta myCtlGridX    ; storage for easy handling.
  1313.  
  1314.     ldy #oCtlGridY
  1315.     lda [<CtlPtr],y
  1316.     sta myCtlGridY
  1317.  
  1318. ; Check the color table pointer. If it is non-zero, install it as the
  1319. ; pointer to the color table we'll be using. If not, then install a
  1320. ; pointer to a default color table.
  1321.  
  1322.     ldy  #octlColor    ; Get the pointer to the color
  1323.     lda [<CtlPtr],y    ; table in the control record.
  1324.     sta ColorPtr    ; Save it to a local pointer.
  1325.     iny
  1326.     iny
  1327.     lda [<CtlPtr],y
  1328.     sta ColorPtr+2
  1329.     ora ColorPtr    ; is it a NULL pointer?
  1330.     bne done    ; no, so use it.
  1331.  
  1332.     lda #StdColorTable    ; yes, install my own color table.
  1333.     sta ColorPtr
  1334.     lda #^StdColorTable
  1335.     sta ColorPtr+2
  1336.  
  1337. done
  1338.     rts
  1339.  
  1340.     ENDP
  1341.  
  1342.     EJECT
  1343. *******************************************************************************
  1344. *
  1345. SetCtlRect    PROC
  1346. *
  1347. * Description:    This routine is called when the control's bounding
  1348. *    rectangle has been changed and needs to be written back
  1349. *    out to the control record.
  1350. *
  1351. *
  1352. * Inputs:    NONE
  1353. *
  1354. * Outputs:    NONE
  1355. *
  1356. * External Refs:    NONE
  1357. *
  1358. * Entry Points:    NONE
  1359. *
  1360. *******************************************************************************
  1361.     with CtlData
  1362.  
  1363.     ldy #octlRect+6    ; get an offset into the control rec.
  1364.     ldx #6    ; index to my local record.
  1365. loop
  1366.     lda myCtlRect,x
  1367.     sta [<CtlPtr],y
  1368.     dey
  1369.     dey
  1370.     dex
  1371.     dex
  1372.     bpl loop
  1373.  
  1374.     rts
  1375.  
  1376.     ENDP
  1377.  
  1378.     EJECT
  1379. *******************************************************************************
  1380. *
  1381. SetMyPen    PROC
  1382. *
  1383. * Description:    We want our control to look good in both 320 and 640 mode.
  1384. *    Because vertical lines are thinner in 640 than 320, its a
  1385. *    good idea to draw them doubly thick just so they can be
  1386. *    seen. This routine will determine what mode we are in and
  1387. *    adjust the pen accordingly.
  1388. *
  1389. *
  1390. * Inputs:    NONE
  1391. *
  1392. * Outputs:    NONE
  1393. *
  1394. * External Refs:    NONE
  1395. *
  1396. * Entry Points:    NONE
  1397. *
  1398. *******************************************************************************
  1399.     with CtlData
  1400.  
  1401.     pha    ; we base pen width on the MasterSCB
  1402.     _GetMasterSCB
  1403.     pla
  1404.     and #$0080    ; check the mode bit
  1405.     beq pen320    ; we are in 320 mode - go push a 1
  1406.  
  1407.     PushWord #2    ; in 640 mode - push on a 2 width
  1408.     bra setpn
  1409.  
  1410. pen320    PushWord #1    ; in 320 mode - push on a 1 width
  1411.  
  1412. setpn        ; width is 1 or 2
  1413.     PushWord #1    ; Height is always 1
  1414.     _SetPenSize
  1415.  
  1416.     rts
  1417.     ENDP
  1418.  
  1419.     EJECT
  1420. *******************************************************************************
  1421. *
  1422. CalcCorners    PROC
  1423. *
  1424. * Description:    This routine calculates the rectangles for all of the
  1425. *    little knobs that are used to grow the control. They are
  1426. *    stored locally - not with the control record - and so need
  1427. *    to be calculated every time we need to check for hits or
  1428. *    to draw the control.
  1429. *
  1430. *
  1431. * Inputs:    NONE
  1432. *
  1433. * Outputs:    knobXXX, FrameHeight, FrameWidth variables are initialized.
  1434. *
  1435. * External Refs:    NONE
  1436. *
  1437. * Entry Points:    NONE
  1438. *
  1439. *******************************************************************************
  1440.     with CtlData
  1441.  
  1442. top    equ 0    ; alias these to something a little
  1443. left    equ 2    ; more descriptive.
  1444. bottom    equ 4
  1445. right    equ 6
  1446.  
  1447. ; Get the size of the knobs. The height and width are stored in the CtlValue
  1448. ; field in a packed format. This unpacks them and stores them locally for
  1449. ; ease of access.
  1450.  
  1451.     ldy #octlValue
  1452.     lda [<CtlPtr],y
  1453.     and #$00FF
  1454.     sta FrameHeight
  1455.     lda [<CtlPtr],y
  1456.     xba
  1457.     and #$00FF
  1458.     sta FrameWidth
  1459.  
  1460. ;
  1461. ; We "assembly line" the initialization of the knob rectangles. Instead
  1462. ; of calculating just one knob at a time, we do a few of them all at the
  1463. ; same time. Here we initialize the tops and bottoms of the 3 top knobs.
  1464.  
  1465.     lda myCtlRect+top
  1466.     sta knobUL+top
  1467.     sta knobTop+top
  1468.     sta knobUR+top
  1469.     clc
  1470.     adc FrameHeight
  1471.     sta knobUL+bottom
  1472.     sta knobTop+bottom
  1473.     sta knobUR+bottom
  1474.  
  1475. ;
  1476. ; Initialize the lefts and rights of the three left knobs.
  1477. ;
  1478.     lda myCtlRect+left
  1479.     sta knobUL+left
  1480.     sta knobLeft+left
  1481.     sta knobLL+left
  1482.     clc
  1483.     adc FrameWidth
  1484.     sta knobUL+right
  1485.     sta knobLeft+right
  1486.     sta knobLL+right
  1487.  
  1488. ;
  1489. ; Initialize the tops and bottoms of the three bottom knobs.
  1490. ;
  1491.     lda myCtlRect+bottom
  1492.     sta knobLL+bottom
  1493.     sta knobBottom+bottom
  1494.     sta knobLR+bottom
  1495.     sec
  1496.     sbc FrameHeight
  1497.     sta knobLL+top
  1498.     sta knobBottom+top
  1499.     sta knobLR+top
  1500.  
  1501. ;
  1502. ; Initialize the lefts and rights of the three right knobs.
  1503. ;
  1504.     lda myCtlRect+right
  1505.     sta knobUR+right
  1506.     sta knobRight+right
  1507.     sta knobLR+right
  1508.     sec
  1509.     sbc FrameWidth
  1510.     sta knobUR+left
  1511.     sta knobRight+left
  1512.     sta knobLR+left
  1513.  
  1514. ;
  1515. ; At this point, the 4 corner knobs have been completely initialized.
  1516. ; If we are going to be using the edge knobs, then we'll have to do
  1517. ; some extra math. However, if we aren't, then we'll skip over the
  1518. ; math.
  1519.  
  1520.     ldy #octlFlag
  1521.     lda [<CtlPtr],y
  1522.     and #knobEdgeMask
  1523.     beq done
  1524.  
  1525. ; Do extra setup for edge knobs
  1526. ;
  1527. ; First, get top and bottom coordinates for the side edges:
  1528. ;
  1529. ;     top = (myCtlRect.top + myCtlRect.bottom - FrameHeight)/2
  1530. ;     bot = (myCtlRect.top + myCtlRect.bottom + FrameHeight)/2
  1531.  
  1532.     clc
  1533.     lda myCtlRect+top
  1534.     adc myCtlRect+bottom
  1535.     pha
  1536.     adc FrameHeight
  1537.     lsr a
  1538.     sta knobLeft+bottom
  1539.     sta knobRight+bottom
  1540.     pla
  1541.     sec
  1542.     sbc FrameHeight
  1543.     lsr a
  1544.     sta knobLeft+top
  1545.     sta knobRight+top
  1546.  
  1547. ;
  1548. ; Now perform similar calculations for the left and right sides
  1549. ; of the top and bottom edge knobs.
  1550. ;
  1551.     clc
  1552.     lda myCtlRect+left
  1553.     adc myCtlRect+right
  1554.     pha
  1555.     adc FrameWidth
  1556.     lsr a
  1557.     sta knobTop+right
  1558.     sta knobBottom+right
  1559.     pla
  1560.     sec
  1561.     sbc FrameWidth
  1562.     lsr a
  1563.     sta knobTop+left
  1564.     sta knobBottom+left
  1565.  
  1566. done
  1567.     rts
  1568.     ENDP
  1569.  
  1570.     EJECT
  1571. *******************************************************************************
  1572. *
  1573. Snap2Grid    PROC
  1574. *
  1575. * Description:    This control defproc lets the application optionally create
  1576. *    an invisible grid based on local coordinates. When this is
  1577. *    done, the 4 corners of the control are always aligned to
  1578. *    the coordinates of this grid. This occurs when we drag or
  1579. *    resize the control, or call MoveControl. Snap2Grip is the
  1580. *    routine that ensures we are on the grid.
  1581. *
  1582. *
  1583. * Inputs:    A = low word of pointer to rectangle to fix
  1584. *    X = high word
  1585. *
  1586. * Outputs:    rectangle is modified in place.
  1587. *
  1588. * External Refs:    NONE
  1589. *
  1590. * Entry Points:    NONE
  1591. *
  1592. *******************************************************************************
  1593.     with CtlData
  1594.  
  1595. oldD    equ 1
  1596. oldB    equ oldD+2
  1597. rectPtr    equ oldB+1
  1598.  
  1599. ;
  1600. ; We are going to use a local direct page here. This is because Snap2Grid is
  1601. ; called by my routine that draws the control's recangle as it is being
  1602. ; dragged. When that happens, we are not using our normal direct page, and
  1603. ; hence can't use any of the values or work locations there. So we set up
  1604. ; another one with it's own workspace.
  1605. ;
  1606.     phx    ; save the pointer to the rectangle
  1607.     pha
  1608.  
  1609.     phb    ; save the caller's data bank register
  1610.     phd    ; save the caller'd direct page reg.
  1611.  
  1612.     phk    ; switch data bank to program bank
  1613.     plb
  1614.  
  1615.     tsc    ; switch direct page to stack pointer
  1616.     tcd
  1617.  
  1618.  
  1619. ;
  1620. ; WhchGridYX is used to help generalize this routine. We support different
  1621. ; spacings in the X and Y directions of the grid. As we loop through all 4
  1622. ; coordinates of the rectangle, we want to make sure that we are using the
  1623. ; correct spacing. 'whichGridYX' holds flags that tell us when to use X and
  1624. ; when to use Y spacing (0 = use Y spacing; 1 = use X spacing).
  1625. ;
  1626.     lda #%0101    ; Set up some indices into GridYX
  1627.     sta whichGridYX
  1628.  
  1629. ;
  1630. ; Fix the coordinates by using the following method:
  1631. ;
  1632. ;  1. Get the difference between the coordinate we are examining and
  1633. ;     the next lowest grid location:
  1634. ;
  1635. ;          z = coordinate mod GridSize
  1636. ;
  1637. ;  2. Z tells how far away from the next lowest grid line
  1638. ;     the coordinate we are looking at is, so subtracting that
  1639. ;     value will put the coordinate on the grid.
  1640. ;
  1641. ;          coordinate := coordinate - z
  1642. ;
  1643. ;  3. However, this is simply truncating, and I don't like the
  1644. ;     way that feels when resizing the control. So lets add an
  1645. ;     instruction so that it ROUNDS instead.
  1646. ;
  1647. ;          if z > GridSize/2 then
  1648. ;               coordinate := coordinate + GridSize
  1649. ;
  1650.  
  1651.     ldy #6    ; init our loop index
  1652. Loop
  1653.     sty YSave
  1654.  
  1655.     lda whichGridYX    ; do we now use X or Y grid spacing?
  1656.     and #%0001
  1657.     beq UseY
  1658.     lda myCtlGridX
  1659.     bra UseIt
  1660. UseY
  1661.     lda myCtlGridY
  1662.  
  1663. ; we now have the gridsize in the Acc
  1664. UseIt
  1665.     cmp #2    ; Is it 0 or 1? If so, then skip over
  1666.     blt ShortCircuit    ;   dividing routine.
  1667.     sta GridSize    ; save this for the divide later.
  1668.  
  1669.     pha    ; push space on stack for the result
  1670.     pha    ; of the divide we are going to do.
  1671.  
  1672.     lda [<rectPtr],y    ; push on the coordinate we are
  1673.     pha    ;   adjusting as the Dividend
  1674.     PushWord GridSize    ; push this on as the divisor
  1675.     _UDivide    ; unsigned divide
  1676.     pla    ; quotient - we don't want it.
  1677.     PullWord Remainder    ; remainder is 'z' - Keep it!
  1678.  
  1679.     ldy YSave    ; get my coordinate index back.
  1680.     lda [<rectPtr],y    ; get the coordinate to change
  1681.     sec
  1682.     sbc Remainder    ; change it
  1683.     sta [<rectPtr],y    ; save it back out
  1684.  
  1685.     lda GridSize    ; Make a GridSize/2
  1686.     lsr a
  1687.     cmp Remainder    ; is remainder < GridSize/2?
  1688.     bge ShortCircuit    ; yes - so we're done
  1689.     lda [<rectPtr],y    ; no - so bump the coordinate
  1690.     clc
  1691.     adc GridSize
  1692.     sta [<rectPtr],y
  1693.  
  1694. ShortCircuit    lsr whichGridYX    ; move the next index into place
  1695.     dey    ; bump down to the next coordinate
  1696.     dey    ;   of the rectangle.
  1697.     bpl Loop
  1698.  
  1699. Exit
  1700.     pld    ; restore caller's Direct Page reg.
  1701.     plb    ; restore data bank register
  1702.     pla    ; remove rect pointer
  1703.     pla
  1704.     rts    ; all done - go bye-bye
  1705.  
  1706. whichGridYX    ds.b 2
  1707. YSave    ds.b 2
  1708. Remainder    ds.b 2
  1709. GridSize    ds.b 2
  1710.  
  1711.     ENDP
  1712.  
  1713.     EJECT
  1714. *******************************************************************************
  1715. *
  1716. FindPart    PROC
  1717. *
  1718. * Description:    This routine is called to find out exactly which part of
  1719. *    the control we clicked in. It first checks to see if we
  1720. *    clicked in any of the grow knobs. If so, then it returns
  1721. *    an 'internal partcode' telling the calling routine which
  1722. *    knob was clicked in. If a knob wasn't clicked in, a check
  1723. *    is made to see if we clicked on the frame. If we are
  1724. *    allowed to click in the interior, a check is made to see
  1725. *    if we clicked there, too.
  1726. *
  1727. *
  1728. * Inputs:    NONE
  1729. *
  1730. * Outputs:    A = internal partcode of part hit (1-10)
  1731. *
  1732. * External Refs:
  1733.     import CalcCorners
  1734. *
  1735. * Entry Points:
  1736.     entry TestFrame    ; called by myTestCtl
  1737. *
  1738. *******************************************************************************
  1739.     with CtlData
  1740.  
  1741.     jsr CalcCorners
  1742.  
  1743. ;
  1744. ; We first check to see if the mouse was clicked in any of the knobs. We do
  1745. ; this by assuming a knob was clicked in, and then checking to see if that was
  1746. ; true. If so, we leave here with the partcode we assumed. If not, we loop
  1747. ; through and check all of the other knobs. If none of the knobs was clicked
  1748. ; in, we see if the frame was clicked on.
  1749.  
  1750.     ldx #LeftPart    ; get last knob's part code
  1751.     ldy #7*8    ; point to last knob's rect
  1752.  
  1753. loop
  1754.     phx    ; save partcode we're checking for...
  1755.     phy    ; and the rect's pointer
  1756.  
  1757.     pha    ; space for _PtInRect result
  1758.     PushLong #theParam    ; push on the point to check
  1759.     lda #^knobUL    ; create a pointer to the rect
  1760.     pha
  1761.     clc    ; Bumping just the low byte is OK
  1762.     tya    ;   unless the GS suddenly lets
  1763.     adc #knobUL    ;   code segments cross bank
  1764.     pha    ;   boundaries
  1765.     _PtInRect
  1766.     pla    ; get result of PtInRect
  1767.     ply    ; retrieve rectangle index
  1768.     plx    ; retrieve part code
  1769.     cmp #0    ; did we score a hit?
  1770.     bne InKnob    ; yes, return with the part code
  1771.     dex    ; move down to next partcode
  1772.     sec    ; move down to next rectangle
  1773.     tya
  1774.     sbc #8
  1775.     tay
  1776.     bpl loop    ; keep going until rect index < 0
  1777.  
  1778.     bra TestFrame    ; not in knobs - try frame
  1779.  
  1780. ;
  1781. ; We just scored a hit on one of the knobs, and X holds the knob number.
  1782. ; However, the application may not have corner or edge knobs active. So
  1783. ; if we clicked on an inactive knob, have to check for that and return
  1784. ; 'inFrame' instead of a knob partcode.
  1785. ;
  1786. InKnob
  1787.     ldy #octlFlag    ; are corner knobs active?
  1788.     lda [<CtlPtr],y
  1789.     and #knobCornerMask
  1790.     bne ckEdge    ; yes - so pass this code on.
  1791.  
  1792.     txa    ; Corners not allowed. Did we hit one?
  1793.     lsr a
  1794.     bcs RetFramePart    ; yes, so return click on Frame instead
  1795.  
  1796. ckEdge
  1797.     ldy #octlFlag    ; are edge knobs active?
  1798.     lda [<CtlPtr],y
  1799.     and #knobEdgeMask
  1800.     bne done    ; yes - so return this code
  1801.  
  1802.     txa    ; Edges not allowed. Did we hit one?
  1803.     lsr a
  1804.     bcs TestFrame    ; no, so pass this code on.
  1805. RetFramePart
  1806.     ldx #FramePart    ; yes, so return click on Frame instead
  1807. done
  1808.     txa    ; put the part code    in the Acc
  1809.     rts
  1810.  
  1811. TestFrame
  1812.  
  1813. ; First test to see    if it is within the    Control's rectangle. It should
  1814. ; be, but let's check anyway...
  1815.  
  1816.     pha
  1817.     PushLong #theParam
  1818.     PushLong #myCtlRect
  1819.     _PtInRect
  1820.     pla
  1821.     beq NotInFrame    ; no in the frame. Signal noPart
  1822.  
  1823. ; Now see if we differentiate between the frame and the interior
  1824.  
  1825.     ldy #octlFlag
  1826.     lda [<CtlPtr],y
  1827.     and #dragIntMask
  1828.     bne RetFramePart    ; nope - everything    drags
  1829.  
  1830. ; Yes we do. Now see if we hit the frame fair and square. Inset the
  1831. ; control's bounding rectangle by the size of the knobs that are on
  1832. ; it to create an inner rectangle. If we fall between those two
  1833. ; rectangles, then I say that you have landed on the frame. If we
  1834. ; got this far, then we know that we are within the outer rectangle.
  1835. ; Now see if we are outside of the inner rectangle.
  1836.  
  1837.     ldx #6    ; make a copy of the control rect
  1838. CopyLoop    lda myCtlRect,x    ; so that we can reduce it.
  1839.     sta innerRect,x
  1840.     dex
  1841.     dex
  1842.     bpl CopyLoop
  1843.  
  1844.     PushLong #innerRect    ; push on pointer to the rect to inset
  1845.     PushWord FrameWidth    ; push on the amounts to inset it by
  1846.     PushWord FrameHeight ; (these vals inited by CalcCorners)
  1847.     _InsetRect
  1848.  
  1849.     pha    ; see if we are outside of the inner
  1850.     PushLong #theParam    ; rectangle
  1851.     PushLong #innerRect
  1852.     _PtInRect
  1853.  
  1854.     pla    ; check the result
  1855.     beq RetFramePart    ; we were between rects!
  1856.  
  1857. NotInFrame    lda #noPart    ; Not *exactly* on the frame.
  1858.     rts
  1859.  
  1860. innerRect    ds.b 8
  1861.  
  1862.     ENDP